﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DCT.AutoUpdate.Code.Pipelines
{
    public class PinelineExecutor : SortedDictionary<string, ExecutorSkeleton>, IDisposable
    {        
        public event EventHandler<PinelineExecutorItemsEventArgs> CountChanged;

        public event EventHandler<PinelineExecutorItemsEventArgs> ExecutorRunInit;
        public event EventHandler<PinelineExecutorItemsEventArgs> ExecutorRunCompleted;
        public event EventHandler<PinelineExecutorItemsErrorEventArgs> ExecutorRunError;

        public event EventHandler<PinelineExecutorEventArgs> PinelineRunInit;
        public event EventHandler<PinelineExecutorEventArgs> PinelineRunCompleted;
        public event EventHandler<PinelineExecutorEventArgs> Disposed;

        private PinelineContext context;
        private List<string> sequenceKeys;

        public PinelineExecutor()
            : base(new ListStringCompare(new List<string>()))
        {            
            context = new PinelineContext();
            sequenceKeys = (this.Comparer as ListStringCompare).Inner;
        }

        /// <summary>
        /// 改造原来的键值排序方式，现在的排序方式就是按照Add的顺序为准
        /// </summary>
        class ListStringCompare : IComparer<string>
        {
            private List<string> inner;
            public ListStringCompare(List<string> inner)
            {
                this.inner = inner;
            }

            public List<string> Inner { get { return this.inner; } }

            public int Compare(string x, string y)
            {
                return inner.IndexOf(x).CompareTo(inner.IndexOf(y));
            }
        }

        public new void Add(string key, ExecutorSkeleton executor)
        {
            sequenceKeys.Add(key);
            base.Add(key, executor);
            this.InvokeEvent(this.CountChanged
                , new PinelineExecutorItemsEventArgs(this, key, executor));
        }

        public new ExecutorSkeleton this[string key]
        {
            get { return base[key]; }
            set
            {
                base[key] = value;
                this.InvokeEvent(this.CountChanged
                    , new PinelineExecutorItemsEventArgs(this, key, value));
            }
        }

        public new bool Remove(string key)
        {
            ExecutorSkeleton val = null;
            if (this.TryGetValue(key, out val))
            {
                if (base.Remove(key))
                {
                    sequenceKeys.Remove(key);
                    this.InvokeEvent(this.CountChanged
                        , new PinelineExecutorItemsEventArgs(this, key, val));

                    return true;
                }
            }

            return false;
        }

        public bool ExitWhenError { get; set; }

        public void Run()
        {
            //初始化
            this.InvokeEvent(this.PinelineRunInit, new PinelineExecutorEventArgs(this));

            foreach (var item in this)
            {
                //执行异常
                this.InvokeEvent(this.ExecutorRunInit
                    , new PinelineExecutorItemsEventArgs(this, item.Key, item.Value));
                bool result = false;
                try
                {
                    result = item.Value.Run(context);
                }
                catch (Exception ex)
                {
                    //执行异常
                    this.InvokeEvent(this.ExecutorRunError
                        , new PinelineExecutorItemsErrorEventArgs(this, item.Key, item.Value, ex));
                    if (!ExitWhenError)
                    {
                        result = true;  //执行异常继续执行                    
                    }
                    else {
                        result = false;
                    }
                }
                finally {
                    this.InvokeEvent(this.ExecutorRunCompleted
                        , new PinelineExecutorItemsRunResultEventArgs(this, item.Key, item.Value, result));
                }
                if (!result)
                {
                    break;
                }
            }
            //执行完成
            this.InvokeEvent(this.PinelineRunCompleted, new PinelineExecutorEventArgs(this));
        }

        public void Dispose()
        {
            this.InvokeEvent(this.Disposed, new PinelineExecutorEventArgs(this));
        }

        public PinelineContext Context {
            get {
                return this.context;
            }
        }

        private void InvokeEvent<T>(EventHandler<T> ev, T args)
            where T : EventArgs
        {
            if (ev != null)
            {
                ev(this, args);
            }
        }
    }
}
